#pragma once
#include "thread.hpp"
#include <vector>
#include <queue>
#include "lockGuard.hpp"
const int g_tthread_num = 3;
template <class T>
class ThreadPool
{
public:
    pthread_mutex_t *getMutex()
    {
        return &lock_;
    }
    bool isEmpty()
    {
        return task_queue_.empty();
    }
    void waitCond()
    {
        pthread_cond_wait(&cond_, &lock_);
    }
    T getTask()
    {
        T t = task_queue_.front();
        task_queue_.pop();
        return t;
    }

public:
    ThreadPool(int thread_num = g_tthread_num) : num_(thread_num)
    {
        pthread_mutex_init(&lock_, nullptr);
        pthread_cond_init(&cond_, nullptr);
        for (int i = 1; i <= g_tthread_num; i++)
        {
            threads_.push_back(new Thread(i, routine, this)); // 创建线程，但此时不是运行线程,注意第三个参数，传入this
        }
    }
    // routine本质是消费的过程,消费队列中的任务
    // 但是由于routine被static修饰，他不能使用或调用非static修饰的变量或函数
    // 所以被static修饰的成员函数无法使用类内成员变量task_queue
    static void *routine(void *args)
    {
        ThreadData *td = (ThreadData *)args; // 注意这里的td和tp名字
        ThreadPool<T> *tp = (ThreadPool<T> *)td->args_;
        while (true)
        {
            T task;
            {
                lockGuard lockguard(tp->getMutex()); // 利用RAII思想上锁,出了这个代码块自动释放锁
                // 线程在waitCond这里等的时候会自动释放获得过的锁，当线程又被唤醒的时候，根据waitCond封装的pthread_cond_wait接口
                // 唤醒后该接口会自动返回锁给线程，所以线程被唤醒时会重新自动持有锁
                while (tp->isEmpty())
                    tp->waitCond();
                // 走到这里，读取任务
                task = tp->getTask(); // 任务队列是共享的-》将任务从共享，拿到自己的私有空间
            }
            // 然后执行这个任务
            task();
        }
    }
    // 运行
    void run()
    {
        for (auto &iter : threads_)
        {
            iter->start();
            std::cout << iter->name() << "启动成功" << std::endl;
        }
    }
    // 推入任务
    void pushTask(const T &task)
    {
        lockGuard lockguard(&lock_);
        task_queue_.push(task);
        // 线程会检测队列中有没有任务，如果消费过程把任务全消费了，
        // 没有任务的时候会在消费过程的检测临界资源那里休眠，所以这里生产完一个任务后
        // 需要从这里唤醒线程，告知线程有任务产生了
        pthread_cond_signal(&cond_);
    }
    // 临时接口，用来测试
    void joins()
    {
        for (auto &iter : threads_)
        {
            iter->join();
        }
    }
    ~ThreadPool()
    {
        for (auto &iter : threads_)
        {
            // iter->join();
            delete iter;
        }
        pthread_mutex_destroy(&lock_);
        pthread_cond_destroy(&cond_);
    }

private:
    std::vector<Thread *> threads_;
    int num_;
    std::queue<T> task_queue_;
    pthread_mutex_t lock_;
    pthread_cond_t cond_;
};
